package com.template.wrapper;

import com.template.common.BusinessException;
import com.template.common.GsonHelper;
import com.template.common.ParameterException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.util.WebUtils;

import java.lang.annotation.Annotation;

/**
 * 返回值统一处理
 */
@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseBody.class;

    /** 判断类或者方法是否使用了 @ResponseResultBody */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE)
                || returnType.hasMethodAnnotation(ANNOTATION_TYPE));
    }

    /** 当类或者方法使用了 @ResponseBody 就会调用这个方法 */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
        if (body instanceof JsonResult) {
            return body;
        }else if(body instanceof String){
            if(((String) body).startsWith("{\"data\"")){
                return body;
            }else{
                return GsonHelper.toJson(JsonResult.succeed(body));
            }
        }else if(body == null){
            if(returnType!=null
                    &&returnType.getParameterType()!=null
                    &&"java.lang.String".equals(returnType.getParameterType().getName())){
                return GsonHelper.toJson(JsonResult.succeed(body));
            }
        }
        return JsonResult.succeed(body);
    }


    /**
     * 提供对标准Spring MVC异常的处理
     *
     * @param ex      the target exception
     * @param request the current request
     */
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<JsonResult<?>> exceptionHandler(Exception ex, WebRequest request) throws Exception {
        HttpHeaders headers = new HttpHeaders();
        //处理自定义异常 ex instanceof XxxException进行判断
        if(ex instanceof MissingServletRequestParameterException){
            return this.handleBadReqException(ex,"请求参数错误", headers, request);
        }else if(ex instanceof BusinessException){
            return this.handleBusinessException(ex,ex.getMessage(),headers,request);
        }else if(ex instanceof ParameterException){
            return this.handleBadReqException(ex,ex.getMessage(),headers,request);
        }
        return this.handleException(ex, headers, request);
    }

    /** 异常类的统一处理(业务错误) */
    protected ResponseEntity<JsonResult<?>> handleBusinessException(Exception ex,String msg, HttpHeaders headers, WebRequest request) {
        log.error("handleException: ", ex);
        JsonResult<?> body = JsonResult.failed(msg);
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /** 异常类的统一处理(请求错误) */
    protected ResponseEntity<JsonResult<?>> handleBadReqException(Exception ex,String msg, HttpHeaders headers, WebRequest request) {
        log.error("handleException: ", ex);
        JsonResult<?> body = JsonResult.failed(msg);
        HttpStatus status = HttpStatus.BAD_REQUEST;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /** 异常类的统一处理(未知的) */
    protected ResponseEntity<JsonResult<?>> handleException(Exception ex, HttpHeaders headers, WebRequest request) {
        log.error("handleException: ", ex);
        JsonResult<?> body = JsonResult.failed("未知异常");
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleExceptionInternal(java.lang.Exception, java.lang.Object, org.springframework.http.HttpHeaders, org.springframework.http.HttpStatus, org.springframework.web.context.request.WebRequest)
     * <p>
     * A single place to customize the response body of all exception types.
     * <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
     * request attribute and creates a {@link ResponseEntity} from the given
     * body, headers, and status.
     */
    protected ResponseEntity<JsonResult<?>> handleExceptionInternal(
            Exception ex, JsonResult<?> body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        return new ResponseEntity<>(body, headers, status);
    }
}